<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Awlsim project file generated by awlsim-0.66.0-pre -->
<awlsim_project date_create="2018-02-09 21:25:51.784572"
                date_modify="2018-07-08 18:00:06.926449"
                format_version="1">
	<!-- CPU core configuration -->
	<cpu>
		<!-- CPU core feature specification -->
		<specs call_stack_size="256"
		       nr_accus="2"
		       nr_counters="256"
		       nr_flags="2048"
		       nr_inputs="128"
		       nr_localbytes="1024"
		       nr_outputs="128"
		       nr_timers="256"
		       parenthesis_stack_size="7" />

		<!-- CPU core configuration -->
		<config clock_memory_byte="-1"
		        cycle_time_limit_us="1000000"
		        ext_insns_enable="1"
		        mnemonics="0"
		        ob_startinfo_enable="0"
		        run_time_limit_us="-1" />
	</cpu>

	<!-- AWL/STL language configuration -->
	<language_awl>
		<!-- AWL/STL source code -->
		<source enabled="1"
		        name="OB 1"
		        type="0"><![CDATA[
ORGANIZATION_BLOCK OB 1
	VAR_TEMP
		OB1_EV_CLASS   : BYTE;          // Bits 0-3 = 1 (Coming event), Bits 4-7 = 1 (Event class 1)
		OB1_SCAN_1     : BYTE;          // 1 (Cold restart scan 1 of OB 1), 3 (Scan 2-n of OB 1)
		OB1_PRIORITY   : BYTE;          // Priority of OB execution
		OB1_OB_NUMBR   : BYTE;          // 1 (Organization block 1, OB 1)
		OB1_RESERVED_1 : BYTE;
		OB1_RESERVED_2 : BYTE;
		OB1_PREV_CYCLE : INT;           // Cycle time of previous OB 1 scan (milliseconds)
		OB1_MIN_CYCLE  : INT;           // Minimum cycle time of OB 1 (milliseconds)
		OB1_MAX_CYCLE  : INT;           // Maximum cycle time of OB 1 (milliseconds)
		OB1_DATE_TIME  : DATE_AND_TIME; // Date and time OB 1 started
	END_VAR
BEGIN
	
	// Run the music player
	CALL "FB_musicPlayer", DB 10  (
		volume		:= 15,
		base_speed	:= T#2000ms,
		song_DB		:= "DB_song",
	)
	
END_ORGANIZATION_BLOCK


DATA_BLOCK DB 10
	"FB_musicPlayer"
BEGIN
END_DATA_BLOCK
]]></source>

		<!-- AWL/STL source code -->
		<source enabled="1"
		        name="player"
		        type="0"><![CDATA[
FUNCTION_BLOCK "FB_musicPlayer"
	TITLE           = PWM based music player
	AUTHOR          : Michael Buesch <m@bues.ch>
	// License	: GNU/GPL 2+

	VAR_INPUT
		volume			: INT;
		base_speed		: TIME;
		song_DB			: BLOCK_DB;
	END_VAR
	VAR
		notes_index		: INT;
		octave_shift		: INT;
		sharp			: BOOL;
		tie			: BOOL;
		wait_time_active	: BOOL;
		wait_timestamp		: TIME;
		prev_note_id		: BYTE;
		prev_note_value		: BYTE;
	END_VAR
	VAR_TEMP
		cur_note		: BYTE;
		cur_note_id		: BYTE;
		cur_note_value		: BYTE;
		base_freq		: DINT;
		deci_hz			: INT;
		period			: WORD;
		remaining_ms		: TIME;
		now			: TIME;
	END_VAR
BEGIN
	// Get the current time
	CALL	SFC 64 (
		RET_VAL := #now
	)

	// Check if we need to wait
	U	#wait_time_active
	SPBN	NWAI
	L	#now
	L	#wait_timestamp
	-D
	SLD	1			// sign extension
	SSD	1			// sign extension
	L	0
	<D
	BEB
	CLR
	=	#wait_time_active
NWAI:	NOP	0


	// Open the song we are playing
	AUF	#song_DB


	// Get current note
	L	#notes_index
	SLW	3
	LAR1
	L	DBB [AR1, P#0.0]
	T	#cur_note


	// End of note table?
	L	#cur_note
	L	B#16#FF
	<>I
	SPB NEND
	L	0
	T	#notes_index
	L	DBB 0
	T	#cur_note
NEND:	NOP	0

	// Extract note ID
	L	#cur_note
	UW	W#16#0F
	T	#cur_note_id

	// Extract note value
	L	#cur_note
	UW	W#16#70
	SRW	4
	T	#cur_note_value

	// If this is a special note, handle it.
	L	#cur_note_id
	L	W#16#F
	==I
	SPB	SPEC

	// Check if the current note is equal to the previous note.
	L	#cur_note_id
	L	#prev_note_id
	==I
	SPB	SAME


	// Tune the note.
	// Get the note frequency in deci-Hz.
TUNE:	AUF	"DB_notes"
	U	#sharp
	SPBN	NOSH
	AUF	"DB_notes_sharp"
NOSH:	L	#cur_note_id
	SLW	4
	LAR1
	L	DBW [AR1, P#0.0]
	T	#deci_hz

	// Octave shift the frequency.
	L	#octave_shift
	L	0
	>=I
	SPB	POSO
	// Negative octave shift
	L	#octave_shift
	NEGI
	L	#deci_hz
	SRW
	SPA	OCTE
	// Positive octave shift
POSO:	L	#octave_shift
	L	#deci_hz
	SLW
OCTE:	T	#deci_hz

	// Calculate the PWM period from the frequency.
	L	#deci_hz
	L	0
	==I
	SPB	NPER
	L	L#20000000		// pwm_baseFreqHz * 10
	L	#deci_hz
	/D
NPER:	T	#period

	// Write period to PWM output hardware
	CALL	"FC_setPWM" (
		period	:= #period,
		volume	:= #volume,
	)

	// Calculate the wait timestamp from the current note value.
	CALL	"FC_calcWaitTime" (
		now			:= #now,
		note_value		:= #cur_note_value,
		base_speed		:= #base_speed,
		wait_timestamp		:= #wait_timestamp,
		wait_time_active	:= #wait_time_active,
	)

	// We are done with this note.
	// Reset all flags.
	CLR
	=	#sharp
	=	#tie
	L	#cur_note_id
	T	#prev_note_id
	L	#cur_note_value
	T	#prev_note_value
	SPA	NEXT


	// If this is a special flags note, handle it.
SPEC:	L	#cur_note_value
	SPL	INVA		// Invalid
	SPA	SHRP		// Activate "sharp" (Kreuz)
	SPA	DOT		// Activate "dot" (Punktierung)
	SPA	TIE		// Activate "tie" (Haltebogen)
	SPA	SHUP		// Shift one octave up
	SPA	SHDN		// Shift one octave down
	SPA	INVA		// Invalid
	SPA	INVA		// Invalid
	SPA	INVA		// Invalid
INVA:	BEA


	// Activate "sharp" (Kreuz)
SHRP:	SET
	=	#sharp
	SPA	NEXT


	// Activate "dot" (Punktierung)
	// Get the value of the previous note (that is the currenly tuned one)
	// and wait for half its time.
DOT:	L	#prev_note_value
	+	1			// Decrease value by 50%
	T	#prev_note_value
	CALL	"FC_calcWaitTime" (
		now			:= #now,
		note_value		:= #prev_note_value,
		base_speed		:= #base_speed,
		wait_timestamp		:= #wait_timestamp,
		wait_time_active	:= #wait_time_active,
	)
	SPA	NEXT


	// Activate "tie" (Haltebogen)
TIE:	SET
	=	#tie
	SPA	NEXT


	// Shift one octave up
SHUP:	L	#octave_shift
	+	1
	T	#octave_shift
	SPA	NEXT


	// Shift one octave down
SHDN:	L	#octave_shift
	+	-1
	T	#octave_shift
	SPA	NEXT


	// The current note is the same as the previous one.
	// If "tie" is not active, deactivate the PWM and wait
	// a tiny amount of time. Then re-activate the note.
	// If "tie" is active, tune the note.
SAME:	U	#tie
	SPB	TUNE
	CALL	"FC_setPWM" (
		period := W#16#0,
		volume	:= #volume,
	)
	L	#now
	L	T#30ms
	+D
	UD	DW#16#7FFFFFFF
	T	#wait_timestamp
	SET
	=	#wait_time_active
	// Set "tie" to re-activate the note
	// after the wait time has passed.
	SET
	=	#tie
	SPB	NINC


	// Increment notes index
NEXT:	L	#notes_index
	+	1
	T	#notes_index
NINC:	BE
END_FUNCTION_BLOCK


FUNCTION "FC_setPWM" : VOID
	VAR_INPUT
		period		: WORD;
		volume		: INT;
	END_VAR
	VAR_TEMP
		volume_shift	: INT;
	END_VAR
BEGIN
	// Calculate the duty cycle shift from the volume.
	// #volume can be 0-15
	L	15
	L	#volume
	-I
	+	1
	T	#volume_shift

	// Write period to PWM output hardware
	L	#period
	T	"pwm_period"

	// Write duty cycle to PWM output hardware
	L	#volume_shift
	L	#period
	SRW
	T	"pwm0"
END_FUNCTION


FUNCTION "FC_calcWaitTime" : VOID
	VAR_INPUT
		now			: TIME;
		note_value		: BYTE;
		base_speed		: TIME;
	END_VAR
	VAR_OUTPUT
		wait_timestamp		: TIME;
		wait_time_active	: BOOL;
	END_VAR
	VAR_TEMP
		remaining_ms		: TIME;
	END_VAR
BEGIN
	// Calculate the remaining time from the note value.
	L	#note_value
	L	#base_speed		// base speed, in milliseconds
	SRW
	T	#remaining_ms

	// Calculate the next timestamp to wait for.
	L	#now
	L	#remaining_ms
	+D
	UD	DW#16#7FFFFFFF
	T	#wait_timestamp
	SET
	=	#wait_time_active
END_FUNCTION

]]></source>

		<!-- AWL/STL source code -->
		<source enabled="1"
		        name="notes"
		        type="0"><![CDATA[
// Note frequencies in 0.1 Hz
DATA_BLOCK "DB_notes"
	STRUCT
		note_freqs	: ARRAY [0 .. 14] OF INT;
	END_STRUCT;
BEGIN
	note_freqs[0]	:= 0;
	note_freqs[1]	:= 2616; // c'
	note_freqs[2]	:= 2937; // d'
	note_freqs[3]	:= 3296; // e'
	note_freqs[4]	:= 3492; // f'
	note_freqs[5]	:= 3919; // g'
	note_freqs[6]	:= 4400; // a'
	note_freqs[7]	:= 4939; // h'
	note_freqs[8]	:= 5233; // c''
	note_freqs[9]	:= 5873; // d''
	note_freqs[10]	:= 6593; // e''
	note_freqs[11]	:= 6985; // f''
	note_freqs[12]	:= 7840; // g''
	note_freqs[13]	:= 8800; // a''
	note_freqs[14]	:= 9878; // h''
END_DATA_BLOCK


// Sharp note frequencies in 0.1 Hz
DATA_BLOCK "DB_notes_sharp"
	STRUCT
		note_freqs	: ARRAY [0 .. 14] OF INT;
	END_STRUCT;
BEGIN
	note_freqs[0]	:= 0;
	note_freqs[1]	:= 2772; // cis'
	note_freqs[2]	:= 3111; // dis'
	note_freqs[3]	:= 3296; // e'
	note_freqs[4]	:= 3700; // fis'
	note_freqs[5]	:= 4153; // gis'
	note_freqs[6]	:= 4662; // ais'
	note_freqs[7]	:= 4939; // h'
	note_freqs[8]	:= 5544; // cis''
	note_freqs[9]	:= 6223; // dis''
	note_freqs[10]	:= 6593; // e''
	note_freqs[11]	:= 7400; // fis''
	note_freqs[12]	:= 8306; // gis''
	note_freqs[13]	:= 9323; // ais''
	note_freqs[14]	:= 9878; // h''
END_DATA_BLOCK

]]></source>

		<!-- AWL/STL source code -->
		<source enabled="1"
		        name="song"
		        type="0"><![CDATA[
// The Free Software Song
// Sadi moma bela loza (Bulgarian folk song)
// Words by Richard Stallman, the Free Software Foundation http://fsf.org/
// Richard Stallman and the Free Software Foundation claim no copyright on this song.
// The official homepage for this song is http://www.gnu.org/music/free-software-song.html

DATA_BLOCK "DB_song"
	STRUCT
		SONG : ARRAY [0 .. 57] OF BYTE;
	END_STRUCT;
BEGIN
	SONG[0] := B#16#29;
	SONG[1] := B#16#38;
	SONG[2] := B#16#27;
	SONG[3] := B#16#26;
	SONG[4] := B#16#27;
	SONG[5] := B#16#38;
	SONG[6] := B#16#37;
	SONG[7] := B#16#2F;
	SONG[8] := B#16#36;
	SONG[9] := B#16#25;
	SONG[10] := B#16#25;
	SONG[11] := B#16#1F;
	SONG[12] := B#16#26;
	SONG[13] := B#16#1F;
	SONG[14] := B#16#2F;
	SONG[15] := B#16#37;
	SONG[16] := B#16#28;
	SONG[17] := B#16#1F;
	SONG[18] := B#16#27;
	SONG[19] := B#16#37;
	SONG[20] := B#16#29;
	SONG[21] := B#16#26;
	SONG[22] := B#16#1F;
	SONG[23] := B#16#16;
	SONG[24] := B#16#29;
	SONG[25] := B#16#2F;
	SONG[26] := B#16#38;
	SONG[27] := B#16#2F;
	SONG[28] := B#16#17;
	SONG[29] := B#16#29;
	SONG[30] := B#16#38;
	SONG[31] := B#16#27;
	SONG[32] := B#16#26;
	SONG[33] := B#16#27;
	SONG[34] := B#16#38;
	SONG[35] := B#16#37;
	SONG[36] := B#16#2F;
	SONG[37] := B#16#36;
	SONG[38] := B#16#25;
	SONG[39] := B#16#25;
	SONG[40] := B#16#1F;
	SONG[41] := B#16#26;
	SONG[42] := B#16#1F;
	SONG[43] := B#16#2F;
	SONG[44] := B#16#37;
	SONG[45] := B#16#28;
	SONG[46] := B#16#1F;
	SONG[47] := B#16#27;
	SONG[48] := B#16#37;
	SONG[49] := B#16#29;
	SONG[50] := B#16#26;
	SONG[51] := B#16#1F;
	SONG[52] := B#16#16;
	SONG[53] := B#16#26;
	SONG[54] := B#16#1F;
	SONG[55] := B#16#2F;
	SONG[56] := B#16#16;
	SONG[57] := B#16#FF;
END_DATA_BLOCK

]]></source>
	</language_awl>

	<!-- Symbol table configuration -->
	<symbols>
		<!-- symbol table source code -->
		<source enabled="1"
		        name="Main table"
		        type="3"><![CDATA[
126,pwm_period              AW 0        WORD
126,pwm0                    AW 2        WORD
126,DB_notes                DB 100      DB 100
126,DB_notes_sharp          DB 101      DB 101
126,DB_song                 DB 1        DB 1
126,FB_musicPlayer          FB 1        FB 1
126,FC_setPWM               FC 1        FC 1
126,FC_calcWaitTime         FC 2        FC 2

]]></source>
	</symbols>

	<!-- Core server link configuration -->
	<core_link>
		<!-- Locally spawned core server -->
		<spawn_local enable="0"
		             interpreters="$DEFAULT"
		             port_range_begin="4183"
		             port_range_end="8278" />

		<!-- Remote server connection -->
		<connect host="pilc"
		         port="4151"
		         timeout_ms="3000" />

		<!-- Transport tunnel -->
		<tunnel local_port="-1"
		        type="1">
			<ssh executable="ssh"
			     port="22"
			     user="pi" />
		</tunnel>
	</core_link>

	<!-- Hardware modules configuration -->
	<hardware>
		<!-- Loaded hardware module -->
		<module name="pixtend">
			<params>
				<param name="analogIn0_10V"
				       value="True" />
				<param name="analogIn0_addr" />
				<param name="analogIn0_nos"
				       value="10" />
				<param name="analogIn1_10V"
				       value="True" />
				<param name="analogIn1_addr" />
				<param name="analogIn1_nos"
				       value="10" />
				<param name="analogIn2_addr" />
				<param name="analogIn2_nos"
				       value="10" />
				<param name="analogIn3_addr" />
				<param name="analogIn3_nos"
				       value="10" />
				<param name="analogIn_kHz"
				       value="125" />
				<param name="analogOut0_addr" />
				<param name="analogOut1_addr" />
				<param name="boardType"
				       value="auto" />
				<param name="digitalIn0_addr" />
				<param name="digitalIn1_addr" />
				<param name="digitalIn2_addr" />
				<param name="digitalIn3_addr" />
				<param name="digitalIn4_addr" />
				<param name="digitalIn5_addr" />
				<param name="digitalIn6_addr" />
				<param name="digitalIn7_addr" />
				<param name="digitalOut0_addr" />
				<param name="digitalOut1_addr" />
				<param name="digitalOut2_addr" />
				<param name="digitalOut3_addr" />
				<param name="digitalOut4_addr" />
				<param name="digitalOut5_addr" />
				<param name="enabled"
				       value="True" />
				<param name="gpio0_addr" />
				<param name="gpio0_pullup"
				       value="False" />
				<param name="gpio1_addr" />
				<param name="gpio1_pullup"
				       value="False" />
				<param name="gpio2_addr" />
				<param name="gpio2_pullup"
				       value="False" />
				<param name="gpio3_addr" />
				<param name="gpio3_pullup"
				       value="False" />
				<param name="inputAddressBase"
				       value="0" />
				<param name="outputAddressBase"
				       value="0" />
				<param name="pollIntMs"
				       value="25" />
				<param name="pwm0A_addr"
				       value="AW 2" />
				<param name="pwm0B_addr" />
				<param name="pwm0_baseFreqHz"
				       value="2000000" />
				<param name="pwm0_mode"
				       value="dutycycle" />
				<param name="pwm0_period"
				       value="AW 0" />
				<param name="pwm1A_addr" />
				<param name="pwm1B_addr" />
				<param name="pwm1_baseFreqHz"
				       value="0" />
				<param name="pwm1_mode"
				       value="dutycycle" />
				<param name="pwm1_period" />
				<param name="relay0_addr" />
				<param name="relay1_addr" />
				<param name="relay2_addr" />
				<param name="relay3_addr" />
				<param name="rs485"
				       value="False" />
			</params>
		</module>
	</hardware>

	<!-- Graphical user interface configuration -->
	<gui>
		<editor autoindent="1"
		        paste_autoindent="1"
		        validation="1" />
	</gui>
</awlsim_project>
